/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include "DAP_config.h"
#include "DAP.h"

// SW Macros

#define PIN_SWCLK_SET PIN_SWCLK_TCK_SET
#define PIN_SWCLK_CLR PIN_SWCLK_TCK_CLR

#define SW_CLOCK_CYCLE() \
    PIN_SWCLK_CLR();     \
    PIN_DELAY();         \
    PIN_SWCLK_SET();     \
    PIN_DELAY()

#define SW_WRITE_BIT(bit) \
    PIN_SWDIO_OUT(bit);   \
    PIN_SWCLK_CLR();      \
    PIN_DELAY();          \
    PIN_SWCLK_SET();      \
    PIN_DELAY()

#define SW_READ_BIT(bit)  \
    PIN_SWCLK_CLR();      \
    PIN_DELAY();          \
    bit = PIN_SWDIO_IN(); \
    PIN_SWCLK_SET();      \
    PIN_DELAY()

#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)

// Generate SWJ Sequence
//   count:  sequence bit count
//   data:   pointer to sequence bit data
//   return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence(uint32_t count, const uint8_t *data)
{
    uint32_t val;
    uint32_t n;

    val = 0U;
    n = 0U;
    while (count--)
    {
        if (n == 0U)
        {
            val = *data++;
            n = 8U;
        }
        if (val & 1U)
        {
            PIN_SWDIO_TMS_SET();
        }
        else
        {
            PIN_SWDIO_TMS_CLR();
        }
        SW_CLOCK_CYCLE();
        val >>= 1;
        n--;
    }
}
#endif

// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
#if (DAP_SWD != 0)
void SWD_Sequence(uint32_t info, const uint8_t *swdo, uint8_t *swdi)
{
    uint32_t val;
    uint32_t bit;
    uint32_t n, k;

    n = info & SWD_SEQUENCE_CLK;
    if (n == 0U)
    {
        n = 64U;
    }

    if (info & SWD_SEQUENCE_DIN)
    {
        while (n)
        {
            val = 0U;
            for (k = 8U; k && n; k--, n--)
            {
                SW_READ_BIT(bit);
                val >>= 1;
                val |= bit << 7;
            }
            val >>= k;
            *swdi++ = (uint8_t)val;
        }
    }
    else
    {
        while (n)
        {
            val = *swdo++;
            for (k = 8U; k && n; k--, n--)
            {
                SW_WRITE_BIT(val);
                val >>= 1;
            }
        }
    }
}
#endif

#if (DAP_SWD != 0)

// SWD Transfer I/O
//   request: A[3:2] RnW APnDP
//   data:    DATA[31:0]
//   return:  ACK[2:0]
#define SWD_TransferFunction(speed) /**/                                              \
    static uint8_t SWD_Transfer##speed(uint32_t request, uint32_t *data)              \
    {                                                                                 \
        uint32_t ack;                                                                 \
        uint32_t bit;                                                                 \
        uint32_t val;                                                                 \
        uint32_t parity;                                                              \
                                                                                      \
        uint32_t n;                                                                   \
                                                                                      \
        /* Packet Request */                                                          \
        parity = 0U;                                                                  \
        SW_WRITE_BIT(1U); /* Start Bit */                                             \
        bit = request >> 0;                                                           \
        SW_WRITE_BIT(bit); /* APnDP Bit */                                            \
        parity += bit;                                                                \
        bit = request >> 1;                                                           \
        SW_WRITE_BIT(bit); /* RnW Bit */                                              \
        parity += bit;                                                                \
        bit = request >> 2;                                                           \
        SW_WRITE_BIT(bit); /* A2 Bit */                                               \
        parity += bit;                                                                \
        bit = request >> 3;                                                           \
        SW_WRITE_BIT(bit); /* A3 Bit */                                               \
        parity += bit;                                                                \
        SW_WRITE_BIT(parity); /* Parity Bit */                                        \
        SW_WRITE_BIT(0U);     /* Stop Bit */                                          \
        SW_WRITE_BIT(1U);     /* Park Bit */                                          \
                                                                                      \
        /* Turnaround */                                                              \
        PIN_SWDIO_OUT_DISABLE();                                                      \
        for (n = DAP_Data.swd_conf.turnaround; n; n--)                                \
        {                                                                             \
            SW_CLOCK_CYCLE();                                                         \
        }                                                                             \
                                                                                      \
        /* Acknowledge response */                                                    \
        SW_READ_BIT(bit);                                                             \
        ack = bit << 0;                                                               \
        SW_READ_BIT(bit);                                                             \
        ack |= bit << 1;                                                              \
        SW_READ_BIT(bit);                                                             \
        ack |= bit << 2;                                                              \
                                                                                      \
        if (ack == DAP_TRANSFER_OK)                                                   \
        { /* OK response */                                                           \
            /* Data transfer */                                                       \
            if (request & DAP_TRANSFER_RnW)                                           \
            {                                                                         \
                /* Read data */                                                       \
                val = 0U;                                                             \
                parity = 0U;                                                          \
                for (n = 32U; n; n--)                                                 \
                {                                                                     \
                    SW_READ_BIT(bit); /* Read RDATA[0:31] */                          \
                    parity += bit;                                                    \
                    val >>= 1;                                                        \
                    val |= bit << 31;                                                 \
                }                                                                     \
                SW_READ_BIT(bit); /* Read Parity */                                   \
                if ((parity ^ bit) & 1U)                                              \
                {                                                                     \
                    ack = DAP_TRANSFER_ERROR;                                         \
                }                                                                     \
                if (data)                                                             \
                {                                                                     \
                    *data = val;                                                      \
                }                                                                     \
                /* Turnaround */                                                      \
                for (n = DAP_Data.swd_conf.turnaround; n; n--)                        \
                {                                                                     \
                    SW_CLOCK_CYCLE();                                                 \
                }                                                                     \
                PIN_SWDIO_OUT_ENABLE();                                               \
            }                                                                         \
            else                                                                      \
            {                                                                         \
                /* Turnaround */                                                      \
                for (n = DAP_Data.swd_conf.turnaround; n; n--)                        \
                {                                                                     \
                    SW_CLOCK_CYCLE();                                                 \
                }                                                                     \
                PIN_SWDIO_OUT_ENABLE();                                               \
                /* Write data */                                                      \
                val = *data;                                                          \
                parity = 0U;                                                          \
                for (n = 32U; n; n--)                                                 \
                {                                                                     \
                    SW_WRITE_BIT(val); /* Write WDATA[0:31] */                        \
                    parity += val;                                                    \
                    val >>= 1;                                                        \
                }                                                                     \
                SW_WRITE_BIT(parity); /* Write Parity Bit */                          \
            }                                                                         \
            /* Capture Timestamp */                                                   \
            if (request & DAP_TRANSFER_TIMESTAMP)                                     \
            {                                                                         \
                DAP_Data.timestamp = TIMESTAMP_GET();                                 \
            }                                                                         \
            /* Idle cycles */                                                         \
            n = DAP_Data.transfer.idle_cycles;                                        \
            if (n)                                                                    \
            {                                                                         \
                PIN_SWDIO_OUT(0U);                                                    \
                for (; n; n--)                                                        \
                {                                                                     \
                    SW_CLOCK_CYCLE();                                                 \
                }                                                                     \
            }                                                                         \
            PIN_SWDIO_OUT(1U);                                                        \
            return ((uint8_t)ack);                                                    \
        }                                                                             \
                                                                                      \
        if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT))                \
        {                                                                             \
            /* WAIT or FAULT response */                                              \
            if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) \
            {                                                                         \
                for (n = 32U + 1U; n; n--)                                            \
                {                                                                     \
                    SW_CLOCK_CYCLE(); /* Dummy Read RDATA[0:31] + Parity */           \
                }                                                                     \
            }                                                                         \
            /* Turnaround */                                                          \
            for (n = DAP_Data.swd_conf.turnaround; n; n--)                            \
            {                                                                         \
                SW_CLOCK_CYCLE();                                                     \
            }                                                                         \
            PIN_SWDIO_OUT_ENABLE();                                                   \
            if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) \
            {                                                                         \
                PIN_SWDIO_OUT(0U);                                                    \
                for (n = 32U + 1U; n; n--)                                            \
                {                                                                     \
                    SW_CLOCK_CYCLE(); /* Dummy Write WDATA[0:31] + Parity */          \
                }                                                                     \
            }                                                                         \
            PIN_SWDIO_OUT(1U);                                                        \
            return ((uint8_t)ack);                                                    \
        }                                                                             \
                                                                                      \
        /* Protocol error */                                                          \
        for (n = DAP_Data.swd_conf.turnaround + 32U + 1U; n; n--)                     \
        {                                                                             \
            SW_CLOCK_CYCLE(); /* Back off data phase */                               \
        }                                                                             \
        PIN_SWDIO_OUT_ENABLE();                                                       \
        PIN_SWDIO_OUT(1U);                                                            \
        return ((uint8_t)ack);                                                        \
    }

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_FAST()
SWD_TransferFunction(Fast)

#undef PIN_DELAY
#define PIN_DELAY() PIN_DELAY_SLOW(DAP_Data.clock_delay)
    SWD_TransferFunction(Slow)

    // SWD Transfer I/O
    //   request: A[3:2] RnW APnDP
    //   data:    DATA[31:0]
    //   return:  ACK[2:0]
    uint8_t SWD_Transfer(uint32_t request, uint32_t *data)
{
    if (DAP_Data.fast_clock)
    {
        return SWD_TransferFast(request, data);
    }
    else
    {
        return SWD_TransferSlow(request, data);
    }
}

#endif /* (DAP_SWD != 0) */
